// <copyright file="ForLoopInstruction.cs" company="Codeplex project Eco.Contrib"> 
// The MIT License (MIT) 
// </copyright> 
// <author>Peter Buchmann</author> 

namespace Eco.Contrib
{
    using System;
    using System.Collections.Generic;
    using Eco.ObjectRepresentation;
    using Eco.Services;
    using Eco.UmlCodeAttributes;

    /// <summary>
    /// ForLoopInstruction: Instruction that represents a for loop
    /// </summary>
    public partial class ForLoopInstruction
    {
        /// <summary>
        /// Executes the function.
        /// </summary>
        /// <param name="serviceProvider">The service provider.</param>
        /// <param name="variableList">The variable list.</param>
        /// <returns>Return value of executed function</returns>
        [UmlElement(Id = "88c9a171-db3d-495f-aae1-b6c714715e40")]
        protected internal override IElement ExecuteFunction(IEcoServiceProvider serviceProvider, IModifiableVariableList variableList)
        {
            int loop = this.LowerBound;
            while (loop <= this.UpperBound)
            {
                IElement constant = serviceProvider.GetEcoService<IVariableFactoryService>().CreateConstant(loop);
                this.LoopVariable.Set(variableList, constant);
                IElement element = LoopedInstruction.Execute(serviceProvider, variableList);
                if (element != null)
                {
                    return element;
                }

                loop++;
            }

            return null;
        }

        /// <summary>
        /// Inits the specified tokens.
        /// </summary>
        /// <param name="tokens">The tokens.</param>
        [UmlElement(Id = "c42db993-ee2f-45e9-8174-17cfbe67ad5e")]
        protected internal override void Init(Dictionary<IList<EslToken>, Type> tokens)
        {
            List<EslToken> tokenList = new List<EslToken>();
            tokenList.Add(new EslToken(EslTokenType.Word, "For"));
            tokenList.Add(new EslToken(EslTokenType.Word, "%"));
            tokenList.Add(new EslToken(EslTokenType.Operator, ":="));
            tokens.Add(tokenList, this.GetType());
        }

        /// <summary>
        /// Parses the specified token list.
        /// </summary>
        /// <param name="tokenList">The token list.</param>
        /// <param name="ecoScript">The eco script.</param>
        /// <param name="fullExpression">if set to <c>true</c> [full expression].</param>
        [UmlElement(Id = "8917fedc-94d9-42f6-b9df-816add048cbc")]
        protected internal override void Parse(IList<EslToken> tokenList, EcoScript ecoScript, bool fullExpression)
        {
            IEcoScriptLanguageService ecoScriptLanguageService = this.ServiceProvider().GetEcoService<IEcoScriptLanguageService>();

            EslToken token = GetNextToken(tokenList);
            RememberEcoScript(ecoScript, token);

            token = GetNextToken(tokenList);
            this.LoopVariable = new Variable(this.ServiceProvider());
            this.LoopVariable.Name = token.TokenValue;

            GetNextToken(tokenList);
            token = GetNextToken(tokenList);
            if (token.TokenType != EslTokenType.Number)
            {
                throw new EslException("number expected at " + token.ToString(), token);
            }

            int returnValue;
            bool bOK = int.TryParse(token.TokenValue, out returnValue);
            if (!bOK)
            {
                throw new EslException("integer expected at " + token.ToString(), token);
            }

            this.LowerBound = returnValue;

            token = GetNextToken(tokenList);
            if ((token.TokenType != EslTokenType.Word) || (token.TokenValue.ToLower() != "to"))
            {
                throw new EslException("'to' expected at " + token.ToString(), token);
            }

            token = GetNextToken(tokenList);
            if (token.TokenType != EslTokenType.Number)
            {
                throw new EslException("number expected at " + token.ToString(), token);
            }

            bOK = int.TryParse(token.TokenValue, out returnValue);
            if (!bOK)
            {
                throw new EslException("integer expected at " + token.ToString(), token);
            }

            this.UpperBound = returnValue;

            token = GetNextToken(tokenList);
            if ((token.TokenType != EslTokenType.Word) || (token.TokenValue.ToLower() != "do"))
            {
                throw new EslException("'do' expected at " + token.ToString(), token);
            }

            // Read loop block
            bool finished = false;
            EslInstruction lastInstruction = null;
            EslInstruction instruction = null;
            do
            {
                instruction = ecoScript.ReadNextInstruction(tokenList, true);
                if (instruction != null)
                {
                    if (lastInstruction == null)
                    {
                        this.LoopedInstruction = instruction;
                    }
                    else
                    {
                        lastInstruction.NextInstruction = instruction;
                    }

                    lastInstruction = instruction;
                }

                finished = (instruction == null) || (tokenList.Count == 0) ||
                    ((tokenList[0].TokenType == EslTokenType.Word) && (tokenList[0].TokenValue.ToLower() == "end"));
            }
            while (!finished);

            token = GetNextToken(tokenList);
            if ((token.TokenType != EslTokenType.Word) || (token.TokenValue.ToLower() != "end"))
            {
                throw new EslException("'end' expected at " + token.ToString(), token);
            }

            if (fullExpression)
            {
                EslToken tokenEnd = GetNextToken(tokenList);
                AssertInstructionEnd(tokenEnd);
            }
        }
    }
}